package facades;

import org.apache.derby.iapi.store.raw.Transaction;

import common.Result;

import hitExceptions.InvalidOperationException;
import coreModel.HIT;
import coreModel.ProductContainer;
import coreModel.ProductGroup;
import coreModel.StorageUnit;
import dataPersistence.TransactionManager;
import dataPersistence.dao.IProductGroupDAO;
import dataPersistence.dao.IStorageUnitDAO;
import dataPersistence.dto.ProductGroupDTO;
import dataPersistence.dto.StorageUnitDTO;



/** A class to be a middleman between the gui and the core model. It represents  
 * much of the functionality of the StorageUnit/ProductContainer hierarchy of the 
 * model to the gui. It accepts core model objects as parameters, calls functions 
 * in the core model, and notifies its observers in the gui.
 */
public class StorageFacade extends java.util.Observable
{
	final static NotificationEnum notificationPG = NotificationEnum.PRODUCT_GROUP; 
	final static NotificationEnum notificationSU = NotificationEnum.STORAGE_UNIT; 
	private static StorageFacade instance;


	/**
	 * Constructor, made private because this class is a singleton
	 */
	private StorageFacade()
	{
		//empty
	}

	/**
	 * Returns the instance of the storageFacade
	 * @return instance of the storage facade
	 */
	public static StorageFacade getInstance()
	{
		if(instance==null)
		{
			instance= new StorageFacade();
		}
		return instance;
	}

	/** Requests that a Storage Unit be added to the model.  
	 * 
	 * @param newStorageUnit An object containing the data to put in the new unit.
	 */
	public void addStorageUnit(StorageUnit newStorageUnit)
	{


		//begin transaction
		Result result = new Result();
		if (!HIT.getInstance().isLoadingFromDataBase())
		{
			result =TransactionManager.begin();
			if(!result.getStatus()) System.err.println(result.getMessage());
		}

		try
		{
			HIT.getInstance().getStorageUnitManager().addStorageUnit(newStorageUnit, false);

			//added to fix load
			if(!HIT.getInstance().isLoadingFromDataBase())
			{
				StorageUnitDTO tempDTO = new StorageUnitDTO(newStorageUnit);

				//Create DAO
				IStorageUnitDAO dao = HIT.getInstance().getFactory().getStorageUnitDAO();

				//update DB
				result=dao.add(tempDTO);
				if(!result.getStatus()) System.err.println(result.getMessage());

			}

			if (!HIT.getInstance().isLoadingFromDataBase())
				TransactionManager.end(result.getStatus());


			this.setChanged();
		} 
		catch (InvalidOperationException e)
		{

			System.err.println("Could not add the specified storage unit "+e.getMessage());
		}


		notifyObservers(new Notification(newStorageUnit, notificationSU,NotificationEnum.ADD));
		this.clearChanged();
	}

	/** Requests that an existing Storage Unit be edited.  
	 * 
	 * @param modifiedStorageUnit An object containing the new data for the unit.
	 */
	public void editStorageUnit(StorageUnit originalUnit, StorageUnit modifiedStorageUnit)
	{

		Result result = new Result();

		//begin transaction
		result=TransactionManager.begin();
		if(!result.getStatus()) System.err.println(result.getMessage());

		//only changes the name and productGroup children
		HIT.getInstance().getStorageUnitManager().
		editStorageUnit(originalUnit, modifiedStorageUnit);


		//Creating DTO
		StorageUnitDTO tempDTO = new StorageUnitDTO(originalUnit);

		//Create DAO
		IStorageUnitDAO dao = HIT.getInstance().getFactory().getStorageUnitDAO();

		//update DB
		result=dao.update(tempDTO);
		if(!result.getStatus()) System.err.println(result.getMessage());


		TransactionManager.end(result.getStatus());		


		this.setChanged();
		notifyObservers(new Notification(originalUnit,notificationSU,NotificationEnum.EDIT));
		this.clearChanged();
	}

	/** Requests that a Storage Unit be deleted from the model.  
	 * 
	 * @param storageUnit An object containing the data to look for and delete.
	 */
	public void deleteStorageUnit(StorageUnit storageUnit)
	{
		//are we already checking that it can be done??

		Result result = new Result();

		//begin transaction
		result=TransactionManager.begin();
		if(!result.getStatus()) System.err.println(result.getMessage());


		try
		{
			HIT.getInstance().getStorageUnitManager().removeStorageUnit(storageUnit);
			HIT.getInstance().getProductManager().removeContainer(storageUnit);
			HIT.getInstance().getItemManager().removeContainer(storageUnit);
			this.setChanged();
		}
		catch(InvalidOperationException e)
		{
			System.err.println("error removing storage Unit");
			e.printStackTrace();
		}


		//Creating DTO
		StorageUnitDTO tempDTO = new StorageUnitDTO(storageUnit);

		//Create DAO
		IStorageUnitDAO dao = HIT.getInstance().getFactory().getStorageUnitDAO();

		//update DB
		result=dao.delete(tempDTO);
		if(!result.getStatus()) System.err.println(result.getMessage());


		TransactionManager.end(result.getStatus());

		notifyObservers(new Notification(storageUnit,notificationSU,NotificationEnum.REMOVE));
		this.clearChanged();
	}

	/** Requests that a Product Group be added to the model inside a specified container.  
	 * 
	 * @param productGroup An object containing the data to put in the new unit.
	 * @param parent The container to put the productGroup into.
	 */
	public void addProductGroup(ProductGroup productGroup, 
			ProductContainer parent)
	{

		//begin transaction
		Result result = new Result();
		if (!HIT.getInstance().isLoadingFromDataBase())
		{
			result =TransactionManager.begin();
			if(!result.getStatus()) System.err.println(result.getMessage());
		}

		try
		{
			parent.addProductGroup(productGroup);

			ProductGroupDTO tempDTO = new ProductGroupDTO(productGroup);
			tempDTO.setParentID(parent.getID());

			//Create DAO
			IProductGroupDAO dao = HIT.getInstance().getFactory().getProductGroupDAO();

			//update DB
			if(!HIT.getInstance().isLoadingFromDataBase())
			{
				result=dao.add(tempDTO);
				if(!result.getStatus()) System.err.println(result.getMessage());

				TransactionManager.end(result.getStatus());
			}


		}
		catch (Exception e)
		{
			//this check is done in the Controller
			System.err.println("error Adding productGroup");
			e.printStackTrace();
		}
		this.setChanged();
		notifyObservers(new Notification(productGroup,notificationPG,NotificationEnum.ADD));
		this.clearChanged();
	}

	/** Requests that an existing Product Group be edited.  
	 * 
	 * @param newProductGroup An object containing the new data for the group.
	 * @param originalProductGroup the original object that is being changed
	 */
	public void editProductGroup(ProductGroup originalProductGroup, ProductGroup newProductGroup)
	{

		Result result = new Result();

		//begin transaction
		result=TransactionManager.begin();
		if(!result.getStatus()) System.err.println(result.getMessage());


		//copy into orginal
		try
		{
			originalProductGroup.setName(newProductGroup.getName());
			originalProductGroup.setParent(newProductGroup.getParent());
			originalProductGroup.setThreeMonths(newProductGroup.getThreeMonths());


			//Creating DTO
			ProductGroupDTO tempDTO = new ProductGroupDTO(originalProductGroup);

			//Create DAO
			IProductGroupDAO dao = HIT.getInstance().getFactory().getProductGroupDAO();

			//update DB
			result=dao.update(tempDTO);
			if(!result.getStatus()) System.err.println(result.getMessage());


			TransactionManager.end(result.getStatus());		



			this.setChanged();
			notifyObservers(new Notification(originalProductGroup,
					notificationPG,NotificationEnum.EDIT));
			this.clearChanged();

		}
		catch(InvalidOperationException e)
		{

			System.err.println(e.getMessage());
			e.printStackTrace();
		}

	}

	/** Requests that a Product Group be removed from the model.  
	 * 
	 * @param productGroup An object containing the data to look for and delete.
	 */
	public void deleteProductGroup(ProductGroup productGroup)
	{

		Result result = new Result();

		//begin transaction
		result=TransactionManager.begin();
		if(!result.getStatus()) System.err.println(result.getMessage());


		try
		{
			HIT.getInstance().getProductManager().removeContainer(productGroup);
			HIT.getInstance().getItemManager().removeContainer(productGroup);
			productGroup.getParent().removeProductGroup(productGroup);
			this.setChanged();
		}
		catch(InvalidOperationException e)
		{
			System.err.println("error removing storage Unit");
			e.printStackTrace();
		} 

		//Creating DTO
		ProductGroupDTO tempDTO = new ProductGroupDTO(productGroup);

		//Create DAO
		IProductGroupDAO dao = HIT.getInstance().getFactory().getProductGroupDAO();

		//update DB
		result=dao.delete(tempDTO);
		if(!result.getStatus()) System.err.println(result.getMessage());


		TransactionManager.end(result.getStatus());		

		notifyObservers(new Notification(productGroup,notificationPG,NotificationEnum.REMOVE));
		this.clearChanged();
	}
}
